'use strict';

var log4js = require('log4js');
var commandLineArgs = require('command-line-args')
var named = require('named');
var spawn = require('child_process').spawn;
var dns = require('dns');
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

var logger = log4js.getLogger('default');
logger.level = 'debug';

if(cluster.isMaster) {
	logger.debug(`master ${process.pid} is running`);
	var workerCc = numCPUs - 2;
	var diedWorkerCc = 0;
	for(var i = 0;i < workerCc;i++) {
		cluster.fork();
	}
	cluster.on('exit', (worker, code, signal) => {
		diedWorkerCc++;
		logger.debug(`worker ${worker.process.pid} died: ${diedWorkerCc} / ${workerCc}`);
		if(diedWorkerCc == workerCc) {
			process.exit(0);
		}
	});
	return;
}

logger.debug(`worker ${process.pid} is running`);

var domain_map = {};
domain_map['1.btc'] = 'jiang.btc.top';//'s9sc.btc.top';
domain_map['2.btc'] = 'bak.btc.top';
domain_map['3.btc'] = 'cn.ss.btc.com';
domain_map['1.ltc'] = 'L3.ltc.top';
domain_map['2.ltc'] = 'bakL3.ltc.top';
domain_map['3.ltc'] = 'cn-ltc.ss.btc.com';
domain_map['vps2.iminer.vip'] = 'vps2.iminer.vip';
domain_map['vps1.iminer.vip'] = 'vps1.iminer.vip';

var btc_port = 20001;
var ltc_port = 20002;

var other_btc = 'stratum.virus.btc.top';
var other_ltc = 'l3.ltc.top';

var optionDefinitions = [
];

var options = commandLineArgs(optionDefinitions);

var ttl = 43200;
var cache = {};

function refresh_cache(domain) {
	logger.debug('refreshing domain: %s', domain);
	dns.lookup(domain, {all:true}, function(err, list) {
		if(err) {
			logger.error('solveRaw failed: %s', domain, err);
			return;
		}

		list = list.filter(x => x.family == 4);
		list = list.map(x => x.address);
		if(list.length == 0) {
			logger.error('solveRaw failed to get IPv4: %s', domain);
			return;
		}

		//console.log(domain, list);
		logger.info('%s updated to %s', domain, list);
		cache[domain] = {
			at: new Date(),
			list: list,
		};
	});
}

function refresh_all_cache() {
	var domains = {};
	for(var k in domain_map) {
		var v = domain_map[k].toLowerCase();
		domains[v] = false;
	}
	domains[other_ltc.toLowerCase()] = false;
	domains[other_btc.toLowerCase()] = false;
	for(var k in domains) {
		refresh_cache(k);
	}
}

refresh_all_cache();
setInterval(refresh_all_cache, 60 * 1000);


function listenOnThisIPAndPort(ip, port, coin) {
	var key = '[' + coin + ' server@' + ip + ':' + port + ']';

	var other = '';
	if(coin == 'btc') {
		other = other_btc;
	} else {
		other = other_ltc;
	}

	var server = named.createServer();

	server.listen(port, ip, function() {
		logger.info('%s started', key);
	});

	server.on('error', function(err) {
		logger.error('%s error', key, err);
	});

	server.on('query', function(query) {
		var domain = (query.name()||'').toLowerCase();
		var client = query._client.address || 'unknown';

		var target = domain_map[domain];
		if(!target) {
			target = other;
		}

		logger.info('%s receive query %s from %s, mapped to %s', key, domain, client, target);
		target = target.toLowerCase();

		if(cache[target]) {
			var list = cache[target].list;
			var idx = Math.random() * list.length;
			idx = Math.floor(idx);
			var ret = list[idx];
			logger.info('   resolved to %s', ret);
			response(query, domain, ret);
			return;
		}

		logger.error('    resolved failed');
		//response(query, domain, '0.0.0.0');
	});

	function response(query, domain, addr) {
		var target = new named.ARecord(addr);
		query.addAnswer(domain, target, ttl);
		server.send(query);
	}	
}

listenOnThisIPAndPort('0.0.0.0', btc_port, 'btc');
listenOnThisIPAndPort('0.0.0.0', ltc_port, 'ltc');

